package controllers;

import java.net.MalformedURLException;
import java.util.*;

import javax.mail.Session;

import models.*;
import play.data.validation.Required;
import play.mvc.*;
import play.mvc.Scope.Params;

@With(Secure.class)
public class Board extends Controller {

	public static void index() {
		List<VirtualBoard> allBoards = VirtualBoard.all().fetch();
		render("Board/index.html", allBoards);
	}

	/**
	 * This method makes a new column and attach it to the board, it takes the
	 * boardId to identify which board you are at and takes the eventId, type
	 * and componentId to be able to reload the board and also takes the new
	 * column's name and status
	 * 
	 * @param boardId
	 *            Board being redesigned
	 * @param eventId
	 *            Event this board is in
	 * @param type
	 *            type of the board
	 * @param componentId
	 *            component ID if available
	 * @param name
	 *            name of the new column
	 * @param status
	 *            status of the new column
	 * @return
	 */
	public static boolean newColumn(long boardId, Long eventId,
			Long componentId, String name, long status) {
		if (name.length() > 50) {
			flash.error("Name must be not more than 50 characters");
			if (componentId == null)
				viewBoard(eventId);
			else
				viewComponentBoard(eventId, componentId);
			return true;

		}

		TaskStatus columnStatus = TaskStatus.findById(status);
		if (name.isEmpty()) {
			name = columnStatus.status;
		}

		VirtualBoard board = VirtualBoard.findById(boardId);
		int position = board.columns.size();
		new BoardColumn(position + 1, columnStatus, name, board).save();

		flash.success("New Column Added !! with name:" + name);
		if (componentId == null)
			viewBoard(eventId);
		else
			viewComponentBoard(eventId, componentId);
		return true;
	}

	/**
	 * This method renames an existing column, it takes the columnId to identify
	 * which column you want to rename and takes the eventId, type and
	 * componentId to be able to reload the board and also takes the column's
	 * new name.
	 * 
	 * @param eventId
	 *            Event this board is in
	 * @param type
	 *            type of the board
	 * @param componentId
	 *            component ID if available
	 * @param columnId
	 *            column being edited
	 * @param newName
	 *            new name of the column
	 * @return
	 */
	public static boolean renameColumn(Long eventId, String type,
			Long componentId, long columnId, String newName) {

		if (newName.length() > 50 || newName.length() <= 0 || newName == null
				|| newName == "") {
			flash.error("Name must be between 1 to 50 characters");

			return false;

		} else {
			BoardColumn column = BoardColumn.findById(columnId);
			flash.success("Column Renamed !!  New Name: " + newName);
			column.name = newName;
			column.save();
			return true;
		}
	}

	/**
	 * This method deletes an existing column, it takes the columnId to identify
	 * which column you want to delete and takes the eventId, type and
	 * componentId to be able to reload the board.
	 * 
	 * @param eventId
	 *            Event this board is in
	 * @param type
	 *            type of the board
	 * @param columnId
	 *            column being edited
	 * @param componentId
	 *            component ID if available
	 * @return
	 */
	public static boolean deleteColumn(Long eventId, String type,
			long columnId, Long componentId) {
		BoardColumn column = BoardColumn.findById(columnId);
		int position = column.positionNumber;
		VirtualBoard board = column.board;
		List<BoardColumn> columns = BoardColumn
				.find("select c from BoardColumn c where c.positionNumber>? and c.board=?",
						position, board).fetch();
		for (int i = 0; i < columns.size(); i++) {
			BoardColumn temp = columns.get(i);
			temp.positionNumber = temp.positionNumber - 1;
			temp.save();
		}
		BoardColumn.findById(columnId).delete();
		flash.success("Column with name " + column.name + " Deleted !!");
		if (componentId == null)
			viewBoard(eventId);
		else
			viewComponentBoard(eventId, componentId);
		return true;
	}

	/**
	 * this Method takes as a parameter the Id of a project and deletes its
	 * snapshots from the DB. It is only used when the project is deleted.
	 * 
	 * @param projectId
	 *            project to delete the snapshots of
	 */
	public static boolean deleteSnapshots(long projectId) {
		List<Event> events = ((Project) Project.findById(projectId)).events;
		if (events != null && !events.isEmpty())
			for (int i = 0; i < events.size(); i++) {
				if (events.get(i).Snapshots != null)
					for (int j = 0; j < events.get(i).Snapshots.size(); j++)
						if (events.get(i).Snapshots.get(0) != null) {
							events.get(i).Snapshots.get(0).delete();
						}
				events.get(i).Snapshots = null;
				events.get(i).save();
			}
		return true;
	}

	/**
	 * this method takes as parameters the Id of an event and the type of the
	 * requested board it is called when a user requests to view the board it
	 * determines if the board is already saved and hence retrieve it or if it
	 * is not yet made and then it calls createBoard to create new and save it
	 * 
	 * @param eventId
	 *            event this board belong to
	 * @param type
	 *            type of the board
	 * 
	 */

	public static boolean viewBoard(Long eventId) {
		Event event = Event.findById(eventId);
		if (event.board == null)
			VirtualBoard.createBoard(event);
		retrieveBoard(event);
		return true;
	}

	/**
	 * This method takes as parameters the event ID and the type of the board
	 * and generated the html view for this board with the columns and gets the
	 * sticky notes and adds them to the board
	 * 
	 * @param event2
	 *            event this board belong to
	 * @param type
	 *            type of the board
	 * 
	 */
	public static void retrieveBoard(Event event) {
		List<BoardColumn> columns = event.board.columns;
		List<TaskStatus> statusNottaken = event.board.getStatusNotTaken();

		List<UserStory> stories = event.userStories;

		List<List<Task>> tasks = getTasks(event);
		List<List<List<Task>>> allTasks = sortByStatus(tasks,
				event.board.getStatus());

		List<Event> currentMeetings = Events.checkForMeeting(event);
		Event currentMeeting = Events.currMeeting(currentMeetings);

		List<User> attending = new ArrayList<User>();
		List<User> absent = new ArrayList<User>();

		if (currentMeeting != null
				&& !currentMeeting.type.type.equals("Component Meeting")) {
			attending = Events.getAssignedUsers(currentMeeting).get(0);
			absent = Events.getAssignedUsers(currentMeeting).get(1);
		} else
			currentMeeting = null;
		Long boardId = (Long) event.board.getEntityId();
		render(allTasks, boardId, event, columns, statusNottaken, attending,
				absent, currentMeeting, stories);
	}

	/**
	 * This method takes as parameters an array of lists of tasks related to
	 * stories and list of statuses and returns 2 dimensional array of lists of
	 * tasks related to stories and sorted by their statuses in lists
	 * 
	 * @param storiesTasks
	 *            array of lists of tasks related to stories
	 * @param status
	 *            list of statuses
	 * @return lists of tasks related to stories sorted by their statuses
	 */
	private static List<List<List<Task>>> sortByStatus(
			List<List<Task>> storiesTasks, List<TaskStatus> status) {
		List<List<List<Task>>> sortedByStatus = new LinkedList();
		for (int i = 0; i < storiesTasks.size(); i++) {
			sortedByStatus.add(new LinkedList<List<Task>>());
			for (int k = 0; k < status.size(); k++) {
				sortedByStatus.get(i).add(new LinkedList<Task>());
				for (int j = 0; j < storiesTasks.get(i).size(); j++) {
					if ((storiesTasks.get(i).get(j)).status.equals(status
							.get(k)))
						sortedByStatus.get(i).get(k)
								.add(storiesTasks.get(i).get(j));
				}
			}
		}
		return sortedByStatus;
	}

	/**
	 * This method takes as a parameter a list of user stories and returns an
	 * list of lists of the tasks related to each story
	 * 
	 * @param stories
	 *            list of user stories
	 * @return array of lists of the tasks
	 */
	private static List<List<Task>> getTasks(Event event) {
		
		List<UserStory> stories = event.userStories;
		List<List<Task>> storiesTasks = new LinkedList();
		for (int i = 0; i < stories.size(); i++) {
			storiesTasks.add(new LinkedList<Task>());
			storiesTasks.get(i).addAll(stories.get(i).tasks);
		}
		List nonStory = controllers.Events.getEventTasksNotHaveUserStory(event);
		storiesTasks.add(new LinkedList<Task>());
		storiesTasks.get(storiesTasks.size() - 1).addAll(nonStory);

		return storiesTasks;
	}

	/**
	 * This method is called to view the virtual board of the component it
	 * determines whether it is already created so it gets it else it creates
	 * it.
	 * 
	 * @param event2
	 *            event this board belong to
	 * @param componentId
	 *            component this board belong to
	 */
	public static void viewComponentBoard(Long eventId, long componentId) {
		Event event = Event.findById(eventId);
		if (event.board == null)
			VirtualBoard.createBoard(event);
		retrieveComponentBoard(event, componentId);
	}

	/**
	 * This method takes as parameters the event ID and the type of the board
	 * and generated the html view for this board with the columns and gets the
	 * sticky notes and adds them to the board
	 * 
	 * @param event2
	 *            event this board belong to
	 * @param type
	 *            type of the board
	 * @param componentId
	 *            component this board belong to
	 * 
	 */

	public static void retrieveComponentBoard(Event event2, long componentId) {
		// Security.checkAccess("View Component Virtual Board",
		// (Long) event2.getEntityId(), Event.class.getName());
		// Event event = Event.findById(event2);
		// Component component = Component.findById(componentId);
		// if (component != null) {
		// long boardId = (Long) event.board.getEntityId();
		// List<BoardColumn> columns = event.board.columns;
		// List<BoardColumn> columns2 = event.board.columns;
		// List<String> Status = getStatus(columns).get(0);
		// List<String> status = getStatus(columns).get(1);
		// Long[] IDS = new Long[columns.size()];
		// for (int i = 0; i < columns.size(); i++) {
		// IDS[i] = ((Long) columns.get(i).status.getEntityId());
		// }
		//
		// // getting StickyNotes for stories
		// List<UserStory> stories = event.userStories;
		// List<UserStory> componentStories = component.userStoriesAssigned;
		// List<UserStory> tmpSt = new ArrayList<UserStory>();
		// for (int i = 0; i < stories.size(); i++) {
		// if (componentStories.contains(stories.get(i)))
		// tmpSt.add(stories.get(i));
		// }
		// stories = tmpSt;
		// List<StickyNote> StoriesStickyNotes = getStoryNotes(stories);
		//
		// List<Task> noStoryTasks = component
		// .getComponentEventTasks((Long) event2.getEntityId());
		// List<List<Task>> storiesTasks = getTasks(stories);
		// List<Task>[][] allTasks = sortByStatus(storiesTasks, status);
		// List<Task>[] sortedNoStoryTasks = new LinkedList[status.size()];
		// for (int i = 0; i < status.size(); i++) {
		// sortedNoStoryTasks[i] = new LinkedList();
		// for (int j = 0; j < noStoryTasks.size(); j++) {
		// if (noStoryTasks.get(j).status.status.equals(status.get(i))) {
		// sortedNoStoryTasks[i].add(noStoryTasks.get(j));
		// break;
		// }
		// }
		// }
		// List[][] TaskStickyNotes = getAllTasks(allTasks,
		// sortedNoStoryTasks, status.size());
		// List<List<List<StickyNote>>> allStickyNotes = fixAllStickynotes(
		// StoriesStickyNotes, TaskStickyNotes);
		//
		// List<Snapshot> snapShots = BoardColumn.find(
		// "select c from Snapshot c where c.event=?", event).fetch();
		//
		// VirtualBoard board = VirtualBoard.findById(boardId);
		// List<TaskStatus> statusNottaken = getStatusNotTaken(event, board);
		//
		// List<Event> currentMeetings = Events.checkForMeeting(event);
		// Event currentMeeting = Events.currMeeting(currentMeetings);
		//
		// List<User> attending = new ArrayList<User>();
		// List<User> absent = new ArrayList<User>();
		// if (currentMeeting != null
		// && currentMeeting.type.type.equals("Component Meeting")) {
		// attending = Events.getAssignedUsers(currentMeeting).get(0);
		// absent = Events.getAssignedUsers(currentMeeting).get(1);
		// } else
		// currentMeeting = null;
		//
		// render(Status, allStickyNotes, boardId, event, snapShots, IDS,
		// columns, statusNottaken, attending, absent, columns2,
		// componentId, currentMeeting);
		// }
	}

	/**
	 * used to take a snapshot from a board and save it in the database with
	 * it's name, URL and event
	 * 
	 * @param eventId
	 *            to identify which event this snapshot belongs to
	 * @param type
	 *            type of the board
	 * @param componentId
	 *            component ID if available
	 * @param name
	 *            name of snapshot
	 * @param url
	 *            URL of snapshot
	 */
	public static boolean snap(Long eventId, String type, Long componentId,
			String name, String url) {
		if (name.length() > 50 || name.length() <= 0 || name == null
				|| name == "" || name == " ") {
			flash.error("Name must be between 1 to 50 characters");
			if (componentId == null)
				viewBoard(eventId);
			else
				viewComponentBoard(eventId, componentId);
			return false;
		}

		flash.success("Snapshot Taken and saved with name: " + name);
		long userId = Long.parseLong(session.get("userId"));
		Event event = Event.findById(eventId);
		Snapshot snap = new Snapshot(name, url, event).save();
		new Log(userId, (Long) snap.getEntityId(), "Snapshot",
				"Took a Snapshot with name " + name,
				(Long) event.project.getEntityId(), " ").save();
		if (componentId == null)
			viewBoard(eventId);
		else
			viewComponentBoard(eventId, componentId);
		return true;
	}

	/**
	 * used to view a chosen snapshot
	 * 
	 * @param snapShot
	 *            SnapShot chosen
	 */
	public static void viewSnap(Snapshot snapShot) {
		String url = snapShot.url;
		render(url);
	}

}